package com.yugao.fintech.antelope.system.handler.config;

import com.alibaba.fastjson2.JSON;
import com.yugao.fintech.antelope.dynamic.config.core.DynamicConfigService;
import com.yugao.fintech.antelope.base.model.enums.ConfigDefineEnum;
import com.yugao.fintech.antelope.base.model.enums.ConfigKeyEnum;
import com.yugao.fintech.antelope.base.model.module.config.param.BaseConfigParam;
import com.yugao.fintech.antelope.system.assembly.SysConfigAssembly;
import com.yugao.fintech.antelope.system.manager.SysConfigManager;
import com.yugao.fintech.antelope.system.model.entity.SysConfig;
import com.yugao.fintech.framework.assistant.utils.StringUtils;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 配置保存或者变更的时候调用, 为什么有这个处理器?
 *
 * 为了实现统一对网站配置, 会直接采用sys_config统一配置表, 然后有一些配置需要同步到某些表中的数据, 比如
 * 网站设置选项, 在配置的时候会需要同步网站名称到租户表中(租户名), 图标同步同理
 * 
 * 目前只有网站设置变更会用到
 */
@Slf4j
public abstract class ConfigHandler {
    private static final Map<ConfigKeyEnum, ConfigHandler> configHandlerMap = new ConcurrentHashMap<>();
    @Autowired
    protected DynamicConfigService configEventService;
    @Autowired
    protected SysConfigAssembly configAssembly;
    @Autowired
    protected SysConfigManager configManager;

    @PostConstruct
    public void init() {
        configHandlerMap.put(configKey(), this);
    }

    public static void handlerWriteOps(SysConfig newConfig) {
        ConfigKeyEnum configKey = ConfigKeyEnum.of(newConfig.getConfigKey());
        if (Objects.isNull(configKey)) {
            return;
        }
        ConfigHandler configHandler = configHandlerMap.get(configKey);
        if (Objects.isNull(configHandler)) {
            configHandler = configHandlerMap.get(ConfigKeyEnum.DEFAULT);
        }

        configHandler.refreshConfig(newConfig);
        configHandler.doneWriteOps(newConfig, BaseConfigParam.jsonToObject(configKey, newConfig.getConfigValue()));
    }

    /**
     * 加工读取操作
     * @param config 配置
     */
    public static void handlerReadOps(SysConfig config) {
        if (Objects.isNull(config)) {
            return;
        }
        if (StringUtils.isEmpty(config.getConfigKey())) {
            log.warn("configKey is empty, no exe read ops");
            return;
        }
        ConfigKeyEnum configKey = ConfigKeyEnum.of(config.getConfigKey());
        if (Objects.isNull(configKey)) {
            return;
        }

        ConfigHandler configHandler = configHandlerMap.get(configKey);
        if (Objects.isNull(configHandler)) {
            return;
        }

        BaseConfigParam<?> oldParam = BaseConfigParam.jsonToObject(configKey, config.getConfigValue());
        config.setConfigValue(JSON.toJSONString(configHandler.doneReadOps(oldParam)));
    }

    /**
     * 获取有效配置, 主要针对 存储配置/短信配置/邮箱配置, 这些配置中, 一般只会使能一种渠道, 比如分别使能 阿里云oss/阿里云SMS/腾讯邮箱
     */
    public static SysConfig getUseConfig(ConfigDefineEnum configDefine) {
        ConfigKeyEnum configKeyEnum = configDefine.getKeys().stream().findFirst().orElse(null);
        if (Objects.isNull(configKeyEnum)) {
            throw new BizException("配置尚未被定义, 请联系管理员");
        }
        ConfigHandler configHandler = configHandlerMap.get(configKeyEnum);
        if (Objects.isNull(configHandler)) {
            log.warn("尚未定义 {} 处理器, 将采用 {} 实现类", configDefine.name(), DefaultConfigHandler.class.getSimpleName());
            configHandler = configHandlerMap.get(ConfigKeyEnum.DEFAULT);
        }
        return configHandler.doGetUseConfig(configDefine);
    }

    /**
     * 获取有效配置, 针对单一配置(比如公众号配置/小程序配置)可以直接使用当前方法, 无需重写 <br/>
     * 但是对于多配置的场景(比如邮箱配置, 分腾讯邮箱、网易邮箱), 而这种场景一般只会使能一个作为发送渠道配置, 因此你需要重写当前方法, 具体例子
     * 看 {@link EmailConfigHandler#doGetUseConfig(ConfigDefineEnum)}
     * @param configDefine 配置定义
     */
    protected SysConfig doGetUseConfig(ConfigDefineEnum configDefine) {
        String configKey = configDefine.getKeys().get(0).getKey();
        if (StringUtils.isEmpty(configKey)) {
            log.warn("从 configDefine [{}] 获取到的configKey是空的", configDefine.name());
            return null;
        }
        return configManager.getByConfigKey(configKey);
    }

    protected void refreshConfig(SysConfig newConfig) {
        log.info("refreshConfig: {}", JSON.toJSONString(newConfig));
        configEventService.publishRefreshConfigEvent(configAssembly.toConfigMsg(newConfig));
    }

    /**
     * 完成写操作
     */
    public abstract void doneWriteOps(SysConfig newConfig, BaseConfigParam<?> param);

    /**
     * 完成读操作
     * @return 返回一个新对象
     */
    protected BaseConfigParam<?> doneReadOps(BaseConfigParam<?> param) {
        return param;
    }


    public abstract ConfigKeyEnum configKey();
}
